#ifndef _H_C_THREAD_POOL_H_
#define _H_C_THREAD_POOL_H_

#include "../typedefine.h"
#include <memory>
#include <functional>
#include "../collection.h"
#include <semaphore>
#include <atomic>
#include <unordered_set>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <list>
#include "../timer.h"
#include "../objpool.h"
#include "CSpinLock.h"
#if !_WIN32
#include <sys/epoll.h>
#endif

namespace TB
{
#if !_WIN32
	using DOnPollEvent = std::function<void(UINT32 dwReturnEvents)>;
#endif

	class CThreadPool
	{
		NON_COPYABLE(CThreadPool)
		NON_MOVEABLE(CThreadPool)
	public:
		typedef void(*DManagerThreadRun)();
		struct SPoller
		{
		public:
			inline void Stop() { m_bStoped = true; }
			inline bool IsStoped() const { return m_bStoped; }
		private:
			friend struct SPollerContainer;
			volatile bool m_bStoped = false;
		};
	private:
		struct SPollerContainer
		{
		public:
			SPollerContainer(const std::function<void()>& cbOnPoll) :
				m_cbOnPoll(cbOnPoll)
			{
			}
		public:
			SPoller m_sPoller;
			std::function<void()> m_cbOnPoll;
		};
#if !_WIN32
	private:
		class CPollEventCallbackObj : public IObjRes
		{
		public:
			void QConstructor(UINT64 qwUID) override
			{
				m_qwUID = qwUID;
			}

			void QDestructor() override
			{
				m_cbOnPollEvent = nullptr;
			}

			inline UINT64 GetUID() const { return m_qwUID; }
		public:
			DOnPollEvent m_cbOnPollEvent;

		private:
			UINT64 m_qwUID = 0;
		};
		static constexpr int MAX_EPOLL_EVENT_COUNT = 10;
#endif
	private:
		struct SThreadPoolInfo
		{
		public:
			const CMPMCQueueNode<std::function<void()>>* m_pcbTaskHead = nullptr;
			INT32 m_dwIdleThreadCountPerSecond = 0;
		};
	public:
		inline static volatile UINT64* GetCurTimePtr() { return &m_qwCurTime; }
		inline static UINT64 GetCurTime() { return m_qwCurTime; }
		inline static std::shared_ptr<CThreadPool> Default() { static auto roDefault = NewThreadPool(); return roDefault; }
		static std::shared_ptr<CThreadPool> NewThreadPool();
		static void DestroyThreadPool(const std::shared_ptr<CThreadPool>& oThreadPool);
		static void StartTimer(UINT64 qwMilliSeconds, const std::function<void()>& cbOnTimer);
		static SPoller& PollPerMilliSecond(const std::function<void()>& cbPollPerMilliSecond);
		static DManagerThreadRun DetachManagerThread();
		static void StopManagerThread();
		static void StartManagerThread();
		static void JoinManagerThread();
		inline static bool IsManagerThreadRunning() { return m_oIsManagerRunning.test(std::memory_order_relaxed); }
#if !_WIN32
		static UINT64 CreatePollEventCallback(const DOnPollEvent& cbOnPollEvent);
		static void ReleasePollEventCallback(UINT64 qwPollEventCallbackUID);
		static bool AddFD(int dwFD, UINT32 dwEvents, UINT64 qwPollEventCallbackUID);
		static bool ModFD(int dwFD, UINT32 dwEvents, UINT64 qwPollEventCallbackUID);
		static bool DelFD(int dwFD);
#endif
		inline UINT32 GetMinThreadCount() const { return m_dwMinThreadCount; }
		inline void SetMinThreadCount(UINT32 dwMinThreadCount) { m_dwMinThreadCount = dwMinThreadCount == 0 ? 1 : dwMinThreadCount; }
		inline UINT32 GetMaxThreadCount() const { return m_dwMaxThreadCount; }
		inline void SetMaxThreadCount(UINT32 dwMaxThreadCount) { m_dwMaxThreadCount = dwMaxThreadCount == 0 ? 1 : dwMaxThreadCount; }
		inline bool IsDefault() const { return m_bIsDefault; }

	private:
		CThreadPool(bool bIsDefault);
	public:
		~CThreadPool();
		void Run(const std::function<void()>& cbFunction);

	private:
		static void _OnPollTimer(const STimerUID& rsTimerUID, UINT32 dwCurrent, UINT32 dwCount);
		static void _OnIncreaseSizeTimer(const STimerUID& rsTimerUID, UINT32 dwCurrent, UINT32 dwCount);
		static void _OnDecreaseSizeTimer(const STimerUID& rsTimerUID, UINT32 dwCurrent, UINT32 dwCount);
		static void _ManagerThreadRun();
		static void _PostManagerTask(const std::function<void()>& cbFunction);
#if !_WIN32
		static void _HandleEpollEvent(int nGot);
#endif
		void _ThreadRun();

	private:
		// thread manager members
		static std::atomic_flag m_oIsManagerRunning;
		static std::unordered_map<std::shared_ptr<CThreadPool>, SThreadPoolInfo> m_oThreadPools;
		static std::unordered_map<std::thread::id, std::shared_ptr<std::thread>> m_oThreads;
		static CMPSCQueue<std::function<void()>> m_oManagerTasks;
		static std::shared_ptr<std::thread> m_poManagerThread;
		static CTimerMgr m_oTimerMgr;
		static std::vector<std::shared_ptr<CThreadPool>> m_oToRemovedThreads;
		static volatile UINT64 m_qwCurTime;
		static std::list<std::shared_ptr<SPollerContainer>> m_oPollPerMilliSecond;
		static volatile bool m_bManagerThreadStoped;
		static std::chrono::steady_clock::time_point m_oStartTime;
		static std::atomic_flag m_oIsManagerTaskQueueNotEmpty;

		// poll
#if _WIN32
		static std::binary_semaphore m_oManagerTaskSemaphore;
#else
		static CObjectResMgr<CPollEventCallbackObj> m_oPollEventCallbackObjMgr;
		static CSpinLockFlag m_oPollEventCallbackSpinLockFlag;
		static int m_nEpollFD;
		static int m_nEventFD;
		static epoll_event m_aEpollEvents[MAX_EPOLL_EVENT_COUNT];
#endif

		// thread members
		UINT32 m_dwThreadCount = 0;
		volatile UINT32 m_dwMinThreadCount = 1;
		volatile UINT32 m_dwMaxThreadCount = 32767;
		std::atomic<UINT32> m_oIdleThreadCount;
		CMPMCQueue<std::function<void()>> m_oTasks;
		std::unique_ptr<std::counting_semaphore<>> m_poSemaphore;
		volatile bool m_bToBeDestroyed = false;
		bool m_bIsDefault = false;
	};
}

#endif
